在Flex/AIR中使用Drag And Drop. Using Drag And Drop in Flex/AIR

Categories: Flex; Tagged with: ; @ September 29th, 2008 23:25

[小站博客均为原创, 转载请保留以下信息:

作者:http://liguoliang.com 欢迎访问:Adobe上海用户组: http://riashanghai.com ]

关于Drag和Drop的基础知识与基本应用可以参见:http://livedocs.adobe.com/flex/3/html/help.html?content=dragdrop_1.html

1. 设计目标

实现Flex/AIR中的Tree,DataGrid自身或相互之间的拖动.

本例仍旧使用NoteManagement,关于NoteManagement的相关信息请参见:
1.完整Flex程序+详细解释之便条管理系统(Tree/回溯/XML/Event) Annotated Flex Sample Application: Note Management
2.使用Air与SQLite开发NoteManagement
3.Flex树形菜单动态加载 Flex Tree Dynamic Loading
通过以上三篇文章,您将对NoteManagement有一个概括性的了解.

2.概要设计:

image

2.1 Tree内目录拖动的实现:
如果使用Tree自带的Drag与Drop属性设定Tree,则目录之间只能实现十分局限的拖 动,如果目录不含有子目录,则其他目录无法拖动到该目录下.要解决这个问题,就必须使用自定义的itemRenderer,该itemRenderer继 承自TreeItemRenderer从而不影响Tree的其他功能的正常使用,相对来说,自定义的itemRenderer应该具有以下功能:1.在有 目录drag到itemRenderer上时他能够acceptDragDrop. 2:可以接受并处理dragDrop事件.

2.2 Note移动的实现:
使用DataGrid的属性dragEnabled属性设定DataGrid可以被drag. 当drag到目录中时,dragEnter的target acceptDragDrop,通过对相关事件的监听进行相应处理.

3.详细实现:

3.1 Tree内目录拖动:
设置Tree的属性为:dragEnabled=”true”  dropEnabled=”false”
在Tree初始化完成后监听DRAG_COMPLETE事件;DragtreeCategory.addEventListener(DragEvent.DRAG_COMPLETE, onCatDragComplete);
通过使用onCatDragComplete可以阻止其默认行为preventDefault(),这样我们获得对DRAG_DROP的控制.
为 Tree建立一个自定义的itemRenderer,该itemRenderer监听DragEvent.DRAG_ENTER与 DragEvent.DRAG_DROP,通过对DRAG_ENTER的监听,控制itemRenderer是否接受Drag,通过对DRAG_DROP 的监听,将DRAG的目录加入到DROP目录中,并删除原目录,以完成整个过程.
3.2 Note的移动
设置DataGrid的属性为dragEnabled=”true”
同3.1在DataGrid初始化完成后监听DRAG_Complete,阻止默认行为并获得控制
当Drag的Note Enter到Tree中时,通过itemRenderer的监听, 控制itemRenderer是否接受DragDROP,并控制后续过程.
自定义itemRenderer的代码如下:

  1. package component
  2. {
  3. import com.insprise.common.sql.Connection;
  4. import com.insprise.common.utility.LogUtils;
  5. import com.insprise.common.utility.SQLUtils;
  6. import com.insprise.nmair.Category;
  7. import com.insprise.nmair.Note;
  8. import flash.data.SQLConnection;
  9. import flash.events.SQLEvent;
  10. import mx.controls.treeClasses.TreeItemRenderer;
  11. import mx.events.DragEvent;
  12. import mx.managers.DragManager;
  13. public class TreeCatItemRender extends TreeItemRenderer
  14. {
  15. public static var dragAndDropClass:int; //辨别Drag来自于Tree还是DataGrid.
  16. private var conn:SQLConnection = new SQLConnection();
  17. private var catMoved:Category;
  18. private var catMoveInto:Category;
  19. private var catEnter:Category;
  20. private var noteMoved:Note;
  21. private var noteMoveIntoCat:Category;
  22. private var noteEnter:Category;
  23. /**
  24. * 构造函数,每次使用该Renderer时会自动添加EventListener;
  25. */
  26. public function TreeCatItemRender() {
  27. super();
  28. this.addEventListener(DragEvent.DRAG_ENTER, dragEnterHandler);
  29. this.addEventListener(DragEvent.DRAG_DROP, dragDropHandler);
  30. }
  31. //在拖动到某一个Render上时,判断是否允许接受Drop;
  32. private function dragEnterHandler(e:DragEvent):void {
  33. if(e.dragSource.hasFormat(“treeItems”)) {
  34. dragAndDropClass = 0;
  35. catMoved = e.dragSource.dataForFormat(“treeItems”)[0] as Category;
  36. catEnter = this.data as Category;
  37. LogUtils.defaultLog.info(“Draged Item: “ + catMoved.label);
  38. LogUtils.defaultLog.info(“Enter Cat: “ + catEnter.label);
  39. if(catMoved.isAncestor(catEnter) || catMoved == catEnter || catMoved.parent == catEnter) {
  40. LogUtils.defaultLog.info(“上级目录不可移动到下级目录, 也不可移动到期自身内部”);
  41. return;
  42. }
  43. DragManager.acceptDragDrop(e.currentTarget as TreeCatItemRender);
  44. }
  45. else if(e.dragSource.hasFormat(“items”)) {
  46. dragAndDropClass = 1;
  47. noteMoved = e.dragSource.dataForFormat(“items”)[0] as Note;
  48. noteEnter = this.data as Category;
  49. if(noteMoved.parent == noteEnter) {
  50. LogUtils.defaultLog.info(“没有进行移动,不进行任何操作”);
  51. return;
  52. }
  53. DragManager.acceptDragDrop(this);
  54. trace(“Note parent.id: “ + noteMoved.parent.id );
  55. trace(” Note Enter Cat id: “ + noteEnter.id);
  56. }
  57. }
  58. //在Drop之后 ,建立与数据库的连接,准备更新数据库.
  59. private function dragDropHandler(e:DragEvent):void {
  60. if(dragAndDropClass == 0) {
  61. e.preventDefault();
  62. catMoveInto = this.data as Category;
  63. LogUtils.defaultLog.info(“移动进: “ + catMoveInto.label);
  64. }
  65. else if(dragAndDropClass == 1) {
  66. e.preventDefault();
  67. noteMoveIntoCat = this.data as Category;
  68. LogUtils.defaultLog.info(“Note: “ + noteMoved.title + ” 被拖进了: “ + noteMoveIntoCat.label);
  69. }
  70. if(conn.connected) {
  71. LogUtils.defaultLog.info(“DataBase have Connected. Call UpdateDB Directly”);
  72. updateDB();
  73. return;
  74. }
  75. LogUtils.defaultLog.info(“Connect Db…..”);
  76. conn.addEventListener(SQLEvent.OPEN, updateDB);
  77. conn.openAsync(Connection.dbFile);
  78. }
  79. //建立 与数据的 连接之后Update改纪录.
  80. private function updateDB(e:SQLEvent=null):void {
  81. if(dragAndDropClass == 0) {
  82. var updateCatSql:String = “UPDATE Cat SET parent_ID='” + catMoveInto.id + “‘ WHERE cat_ID
  83. =” + catMoved.id;
  84. SQLUtils.createAndExecuteStatement(conn, updateCatSql, null, updateDBSucess)
  85. }
  86. else if(dragAndDropClass == 1) {
  87. var updateNotesql:String = “UPDATE Note SET parent_ID='” + noteMoveIntoCat.id +
  88. “‘ WHERE note_ID=” + noteMoved.id;
  89. SQLUtils.createAndExecuteStatement(conn, updateNotesql, null, updateDBSucess)
  90. }
  91. }
  92. //Update成功后,移动目录或note
  93. private function updateDBSucess(e:SQLEvent):void {
  94. if(dragAndDropClass == 0) {
  95. LogUtils.defaultLog.info(catMoved.label + ” 的parent_ID已成功更新为: “ + catMoveInto.id);
  96. catMoveInto.addSubCat(catMoved);
  97. LogUtils.defaultLog.info(“目录: “ + catMoveInto.label + ” 成功添加子目录: “ + catMoved.label);
  98. }
  99. else if(dragAndDropClass == 1) {
  100. LogUtils.defaultLog.info(noteMoved.title + “的parent_ID已成功更新为:  “ + noteMoveIntoCat.id);
  101. noteMoveIntoCat.addNote(noteMoved);
  102. LogUtils.defaultLog.info(“目录: “ + noteMoveIntoCat.label + ” 成功添加Note: “ + noteMoved.title);
  103. }
  104. }
  105. }
  106. }

在move情况下,我们把目标加入到Drop target中,并从Drag initiator中删除,使用dragDrop事件的监听函数来加入目标到Drop target, 使用dragComplete事件的监听函数来删除drag initiator中的源数据.

在这个例子中,由于在 addCat()与addNote()中以含有删除功能,故我们阻止了dragComplete,使用dragDrop处理增加与删除.addCat() 与addNote通过判断源数据的parent是否等于Drop target的id来进行对应处理.

在移动后元数据的parent与Drop target中的Category是不同的,这种情况下先将其删除,修改其parent属性后再加入到Drop target的Category中.完成操作.

代码如下:

  1. /**
  2. * Adds the given child.
  3. * If the child has an existing parent, it will be removed from its parent first then added to this.
  4. */
  5. public function addSubCat(child:Category):void {
  6. if(this.subCats == null) {
  7. subCats = new ArrayCollection();
  8. }
  9. if(child.parent == null) { // initial
  10. child._parent = this;
  11. subCats.addItem(child);
  12. }else{ // not null
  13. if(child.parent == this) {
  14. throw new Error(“The parent has already been set to this: “ + toString() + “, child: “ + child);
  15. }else{ // change parent.
  16. child._parent.delCat(child);
  17. child._parent = this;
  18. subCats.addItem(child);
  19. }
  20. }
  21. }
  22. //删除子目录.须提供一个index,通过findCatIndex函数寻找.
  23. /**
  24. * Remove the given child category and set the removed category’s parent to null.
  25. * @throw Error if the child’s parent is not this.
  26. */
  27. public function delCat(child:Category):void {
  28. trace(“This.id: “ + this.id);
  29. trace(“child’s Parent ID: “ + child.parent.id);
  30. trace(child._parent.subCats.getItemIndex(child));
  31. if(child._parent != this) {
  32. throw new Error(“The parent of the child is not this: “ + toString() + “, child: “ + child);
  33. }
  34. subCats.removeItemAt(subCats.getItemIndex(child));
  35. child._parent = null;
  36. }
  37. /**
  38. * 增加Note
  39. * 如果category的notes为空,则新建notes
  40. * 如果note的parent为空,则将this赋给note的parent  [Note初始化时发生]
  41. * 如果note的parent为this,则为重复添加.报错处理      [重复添加Note,报错]
  42. * 如果note的parent不等于this,则说明是移动过来的note,需要在该note原先的parent下删除该note,并将该note添加到this的notes中.
  43. */
  44. public function addNote(note:Note):void {
  45. if(notes == null) {
  46. notes = new ArrayCollection();
  47. else if(note.parent == null) {
  48. note.parent = this;
  49. notes.addItem(note);
  50. else{
  51. if(note.parent == this) {
  52. throw new Error(note.title + ” 的parent已经被设定为” + this.label);
  53. else{
  54. note.parent.delNote(note);
  55. note.parent = this;
  56. notes.addItem(note);
  57. }
  58. }
  59. }
  60. /**
  61. * 删除note
  62. * 如果note的parent不为this,则报错
  63. * 否则删除该note
  64. * 并将note parent为空.
  65. */
  66. public function delNote(note:Note):void {
  67. if(note.parent != this) {
  68. throw new Error(note.title + ” note的parent不是this “ + this.label);
  69. }
  70. notes.removeItemAt(notes.getItemIndex(note));
  71. note.parent = null;
  72. }

<->



// Proudly powered by Apache, PHP, MySQL, WordPress, Bootstrap, etc,.